package com.itheima.stock.service.impl;

import com.alibaba.excel.EasyExcel;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.itheima.stock.common.StockInfoConfig;
import com.itheima.stock.common.domain.*;
import com.itheima.stock.mapper.StockBlockRtInfoMapper;
import com.itheima.stock.mapper.StockMarketIndexInfoMapper;
import com.itheima.stock.mapper.StockRtInfoMapper;
import com.itheima.stock.service.StockService;
import com.itheima.stock.utils.DateTimeUtil;
import com.itheima.stock.vo.PageResult;
import com.itheima.stock.vo.R;
import com.itheima.stock.vo.ResponseCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.MapKey;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class StockServiceImpl implements StockService {

    //加载大盘代码常量
    @Autowired
    StockInfoConfig stockInfoConfig;

    //加载mapper
    @Autowired
    StockMarketIndexInfoMapper stockMarketIndexInfoMapper;

    //注入国内版块指数对象
    @Autowired
    StockBlockRtInfoMapper stockBlockRtInfoMapper;

    @Autowired
    StockRtInfoMapper stockRtInfoMapper;


    /**
     * 查询国内大盘指数
     *
     * @return
     */
    @Override
    public R<List<InnerMarketDomain>> getInnerStockMarket() {
        /**
         * 1.获取国内大盘的id集合
         * 2.获取最近最新的股票有效交易日，
         *     注： 此处使用工具类判断是否为工作日
         *          但没有实际数据，暂时用固定数据判断
         * 3.调用mapper查询指定日期下对应的国内大盘数据
         * 4.封装响应的数据
         * 使用的工具类：DateTimeUtil
         *              判断是否为工作日，是工作日返回前一天的收盘价格
         *              不是工作日返回上一次工作日的收盘价格，精确到开盘及结束时间
         *             CollectionUtils
         *              Spring框架的工具类，用于判断是否为空
         */
        List<String> inner = stockInfoConfig.getInner();
        //TODO 后续改进，此处先用固定时间代替
        // String lastDate = DateTimeUtil.getLastDateString4Stock(DateTime.now());
        String lastDate = "20211226105600";
        //获取数据交互层的数据
        List<InnerMarketDomain> innerMarketDomainList = stockMarketIndexInfoMapper.selectByIdsAndDate(inner, lastDate);
        //判断是否为空,保证代码健壮性
        if (CollectionUtils.isEmpty(innerMarketDomainList)) {
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        return R.ok(innerMarketDomainList);
    }


    /**
     * 国内板块指数业务实现
     *
     * @return
     */
    @Override
    public R<List<SectorAll>> getSectorAll() {
        List<SectorAll> sectorAllLimit = stockBlockRtInfoMapper.getSectorAllLimit();
        if (CollectionUtils.isEmpty(sectorAllLimit)) {
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        return R.ok(sectorAllLimit);
    }

    /**
     * 涨幅榜功能业务
     * 沪深两市个股涨幅分时行情数据查询，以时间顺序和涨幅查询前10条数据
     *
     * @return
     */
    @Override
    public R<List<StockIncrease>> getStockIncrease() {
        List<StockIncrease> stockIncrease = stockRtInfoMapper.getStockIncrease();
        if (CollectionUtils.isEmpty(stockIncrease)) {
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        return R.ok(stockIncrease);
    }

    /**
     * 分页查询涨幅榜
     *
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public R<PageResult<StockIncrease>> getStockAll(Integer page, Integer pageSize) {
        //设置分页
        PageHelper.startPage(page, pageSize);
        List<StockIncrease> stockAll = stockRtInfoMapper.getStockAll();
        if (CollectionUtils.isEmpty(stockAll)) {
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        //封装进入PageResult
        PageInfo<StockIncrease> pageInfo = new PageInfo<>(stockAll);
        PageResult<StockIncrease> result = new PageResult<>(pageInfo);
        return R.ok(result);
    }

    /**
     * 沪深两市涨跌停分时行情数据查询，查询T日每分钟的涨跌停数据
     *
     * @return
     */
    @Override
    public R<Map> getStockUpDownCount() {

        //从工具类中获取当前有效交易日期
        String string4Stock = DateTimeUtil.getLastDateString4Stock(DateTime.now());
        //TODO 暂时没有数据，使用其他数据代替
        string4Stock = "20220106092500";
        //1 代表涨停
        List<Map> upLimitCount = stockRtInfoMapper.getUpDownLimitCount(1, string4Stock);
        //0 代表跌停
        List<Map> downLimitCount = stockRtInfoMapper.getUpDownLimitCount(0, string4Stock);
        //封装
        HashMap<String, List<Map>> map = new HashMap<>();
        map.put("upList", upLimitCount);
        map.put("downList", downLimitCount);
        return R.ok(map);
    }

    /**
     * 涨幅榜数据分页导出到excel
     *
     * @param response 用于响应excel文件
     * @param page     当前页
     * @param pageSize 每页大小
     */
    @Override
    public void exPort(HttpServletResponse response, Integer page, Integer pageSize) {
        try {
            //1.设置响应数据的类型:excel
            response.setContentType("application/vnd.ms-excel");
            //2.设置响应数据的编码格式
            response.setCharacterEncoding("utf-8");
            //3.设置默认的文件名称
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            String fileName = URLEncoder.encode("stockRt", "UTF-8");
            //设置默认文件名称
            response.setHeader("content-disposition", "attachment;filename=" + fileName + ".xlsx");

            //分页查询
            PageHelper.startPage(page, pageSize);
            List<StockIncrease> stockAll = stockRtInfoMapper.getStockAll();

            EasyExcel.write(response.getOutputStream(), StockIncrease.class).sheet("股票数据").doWrite(stockAll);
        } catch (IOException e) {
            log.info("股票excel数据导出异常，当前页：{}，每页大小：{}，异常信息：{}", page, pageSize, e.getMessage());
        }
    }

    /**
     * 统计国内A股大盘T日和T-1日成交量对比功能（成交量为沪市和深市成交量之和）
     *
     * @return
     */
    @Override
    public R<Map> stockTradeVol4InnerMarket() {
        //TODO 获取当前时间，由测试数据代替
        String nowTime = DateTimeUtil.getLastDateString4Stock(DateTime.now());
        nowTime = "2022-01-03 14:30:00";
        //TODO 前一天的时间 ，由测试数据代替
        //DateTime timeForward = timeNow.minusDays(1); 工具类封装了
        String forwardTime = DateTimeUtil.parseToString4Stock(DateTimeUtil.getPreviousTradingDay(DateTime.now()));
        forwardTime = "2022-01-02 14:30:00";

        //获取数据
        List<Map> now = stockRtInfoMapper.stockTradeVol4InnerMarket(nowTime);
        List<Map> forward = stockRtInfoMapper.stockTradeVol4InnerMarket(forwardTime);

        //封装
        HashMap<String, List<Map>> map = new HashMap<>();
        map.put("volList", now);
        map.put("yesVolList", forward);
        return R.ok(map);
    }

    /**
     * 统计在当前时间下（精确到分钟），股票在各个涨跌区间的数量
     * 需要做到区间划分并排序
     *
     * @return
     */
    @Override
    public R<Map> getStockUpDown() {
        //获取当前时间
        String timeNow = DateTimeUtil.getLastDateString4Stock(DateTime.now());
        //TODO 测试用数据替代
        timeNow = "2022-01-06 09:55:00";
        List<Map> stockUpDown = stockRtInfoMapper.getStockUpDown(timeNow);

        /**
         * 当前在前端查询的数据是无序展示的，需要后端对数据进行合理排序，这样前端才能顺序展示
         * 在yml中顺序定义股票涨幅范围集合
         * 之前的无序map判断并加入为空的数据，存入新的有序集合
         */
        //获取排序好的区间集合
        List<String> upDownRange = stockInfoConfig.getUpDownRange();

/*        //lambda表达式+Stream流写法
        List<Map> collect = upDownRange.stream().map(key -> {
            Optional<Map> title = stockUpDown.stream().filter(map -> key.equals(map.get("title"))).findFirst();
            Map map = null;
            if (title.isPresent()) {
                map = title.get();
            } else {
                map = new HashMap();
                map.put("count", 0);
                map.put("title", key);
            }
            return map;
        }).collect(Collectors.toList());
*/

        //普通for方法
        //定义新集合储存排序区间
        ArrayList<Map> list = new ArrayList<>();
        //排序好的集合，进行判断存入
        for (String s : upDownRange) {
            Map map = null;
            //遍历未排序的map集合，如何如果存在数据，则与排序集合匹配，按顺序赋值给map集合并存入List有序集合中
            for (Map maps : stockUpDown) {
                if (maps.get("title").equals(s)) {
                    map = maps;
                }
            }
            //不存在数据，则创建一个map赋值count为0
            if (map == null) {
                map = new HashMap();
                map.put("title", s);
                map.put("count", 0);
            }
            list.add(map);
        }

        //封装
        //因为封装结果里有String类型和Map类型所以使用Object完事
        HashMap<String, Object> objectHashMap = new HashMap<>();
        objectHashMap.put("time",timeNow);
        objectHashMap.put("infos",list);
        return R.ok(objectHashMap);
    }

    /**
     * 查询指定时间下股票的涨跌幅度区间统计功能
     * 如果当前日期不在有效时间内，则以最近的一个股票交易时间作为查询时间点
     * 参数为指定某一个股票
     * @param code
     * @return
     */
    @Override
    public R<List<StockScreenTimeSharing>> stockScreenTimeSharing(String code) {
        //获取当前时间
        String nowDate = DateTimeUtil.getLastDateString4Stock(DateTime.now());
        //TODO 测试数据代替
        nowDate = "20211226105600";
        List<StockScreenTimeSharing> stockScreenTimeSharings = stockRtInfoMapper.stockScreenTimeSharing(nowDate, code);
        return R.ok(stockScreenTimeSharings);
    }

    /**
     * 单个个股日K数据查询 ，可以根据时间区间查询数日的K线数据
     * 默认查询历史20天的数据；
     * @param code
     * @return
     */
    @Override
    public R<List<DayKLinData>> getDayKLinData(String code) {
        //获取当前时间
        DateTime nowDate = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //获取前20天的日期
        DateTime hisDate = nowDate.minusDays(20);
        String hisDateStr = DateTimeUtil.parseToString4Stock(hisDate);

        List<DayKLinData> dayKLinData = stockRtInfoMapper.getDayKLinData(hisDateStr, code);
        return R.ok(dayKLinData);
    }
}
